package com.itzhouwei.commerce.nacos;
/***
 *
 * @作者 伟子涵
 * @微信公号 搬砖暗夜码农
 * @开源项目 $ https://gitee.com/zhouwei5208888
 * @我的博客 $ http://itzhouwei.com
 * @创建时间 2024/1/8  10:13
 */
//package cn.cws.fulimall.nacos;
//

import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @description:
 * @author：zw，微信：yingshengzw
 * @date: 2024/1/8  10:13
 * @Copyright： 公众号：搬砖暗夜码农 | 博客：https://itzhouwei.com - 沉淀、分享、成长，让自己和他人都能有所收获！
 */
@Component
@Slf4j
public class NacosDiscoveryListener {

    private final Set<String> subscribedServices = ConcurrentHashMap.newKeySet();


    private final NacosServiceManager nacosServiceManager;
    private static Map<String, Map<String, Boolean>> instanceHealthStatus = new ConcurrentHashMap<>();
//    @Autowired
//    private ApplicationNotice applicationNotice;
    // 由devops或者运维平台在执行流水线的时候传进来，给通知使用
    @Value("${spring.application.name}")
    private String projectName;
    @Value("${spring.profiles.active}")
    private String projectEnv;

    @Value("${dingtalk.webhook}")
    private String webhook;
    @Value("${dingtalk.secret}")
    private String secret;


    public NacosDiscoveryListener(NacosServiceManager nacosServiceManager) {
        this.nacosServiceManager = nacosServiceManager;
    }


    /**
     * 构造一个事件监听器，主要作用是监听服务实例变化
     *
     * @return EventListener
     */
    private EventListener buildEventListener() {

        return event -> {
            if (event instanceof NamingEvent) {
                NamingEvent namingEvent = (NamingEvent) event;
                log.trace("服务实例变化:{}", JSON.toJSONString(namingEvent));
                String serviceName = namingEvent.getServiceName();
                if (!instanceHealthStatus.containsKey(serviceName)) {
                    ConcurrentHashMap<String, Boolean> instanceMap = new ConcurrentHashMap<>();
                    instanceHealthStatus.put(serviceName, instanceMap);
                    List<Instance> newInstance = namingEvent.getInstances();
                    newInstance.forEach(instance -> {
                        String instanceKey = instance.getIp() + ":" + instance.getPort();
                        instanceMap.put(instanceKey, instance.isHealthy());
                        log.trace("服务首次上线: {} -> {}", serviceName, instanceKey);
                    });
                    return;
                }
                List<ServiceInfo> allServiceInstances = getAllServiceInstances();
                int instanceTotal = allServiceInstances.stream()
                        .mapToInt(serviceInfo -> Integer.parseInt(serviceInfo.getClusters()))
                        .sum();
                Map<String, Boolean> serviceMap = instanceHealthStatus.computeIfAbsent(serviceName, k -> new ConcurrentHashMap<>());
                Set<String> oldInstanceKeys = new HashSet<>(serviceMap.keySet());
                List<Instance> newInstance = namingEvent.getInstances();
                Set<String> newInstanceKeys = newInstance.stream()
                        .map(instance -> instance.getIp() + ":" + instance.getPort())
                        .collect(Collectors.toSet());
                int oldSize = serviceMap.size();
                int newSize = namingEvent.getInstances().size();
                StringBuffer sb=new StringBuffer();
                // 服务实例没有增减，只是状态变化
                if (oldSize == newSize) {

                    newInstance.forEach((instance) -> {
                        String instanceKey = instance.getIp() + ":" + instance.getPort();

                        if (instance.isHealthy() != serviceMap.get(instanceKey)) {
                            if (instance.isHealthy()) {
                                log.trace("服务上线: {} -> {}", serviceName, instanceKey);
                                sb.append(serviceName+"服务上线通知")
                                        .append(",").append(projectName).append(",").append(projectEnv).append(",")
                                        .append(allServiceInstances.size()).append(",")
                                        .append(instanceTotal).append(",").append(allServiceInstances);
                                sendMsg(sb.toString());
                            } else {
                                log.trace("服务下线: {} -> {}", serviceName, instanceKey);
                                sb.append(serviceName+"服务下线通知")
                                        .append(projectName).append(",").append(projectEnv).append(",").append(allServiceInstances.size())
                                        .append(instanceTotal).append(",").append(allServiceInstances);
                                sendMsg(sb.toString());
                            }
                            serviceMap.put(instanceKey, instance.isHealthy());
                        }
                    });
                }
                // 下线实例
                if (oldSize > newSize) {
                    newInstanceKeys.forEach(oldInstanceKeys::remove);
                    oldInstanceKeys.forEach(instanceKey -> log.info("服务下线: {} -> {}", serviceName, instanceKey));

                    sb.append(serviceName+"服务下线通知")
                            .append(",").append(projectName).append(",").append(projectEnv).append(",").append(allServiceInstances.size())
                            .append(instanceTotal).append(",").append(allServiceInstances);
                    System.err.println("sb = " + sb);
                    sendMsg(sb.toString());
                   // applicationNotice.sendNacosServiceMessage(serviceName+"服务下线通知",projectName, projectEnv, allServiceInstances.size(), instanceTotal, allServiceInstances);
                } else {
                    // 上线实例
                    newInstanceKeys.removeAll(oldInstanceKeys);
                    StringBuffer noticeTitle = new StringBuffer("服务上线通知");
                    newInstanceKeys.forEach(instanceKey -> {
                        String message = String.format("[%s-%s]", serviceName, instanceKey);
                        log.info(message);
                        noticeTitle.append(message).append(",");
                    });
                    sb.append(serviceName+"服务上线通知"+noticeTitle)
                            .append(",").append(projectName).append(",").append(projectEnv).append(",").append(allServiceInstances.size())
                            .append(instanceTotal).append(",").append(allServiceInstances);
                    sendMsg(sb.toString());
                   // applicationNotice.sendNacosServiceMessage(serviceName+"服务上线通知"+noticeTitle,projectName, projectEnv, allServiceInstances.size(), instanceTotal, allServiceInstances);

                }
            }
        };
    }
    /**
     * 定时获取服务列表,然后根据获取到的服务名，进行订阅，nacos客户端目前不能订阅所有服务，只能手动的订阅
     * 也可以不用定时需要的时候通过getAllServiceInstances获取
     */
    @Scheduled(fixedDelay = 10000)
    public void reportServices() {
        List<String> services = null;
        try {
            NamingService namingService = nacosServiceManager.getNamingService();

            services = namingService.getServicesOfServer(1, Integer.MAX_VALUE).getData();
            services.forEach(serviceName -> {
                if (!subscribedServices.contains(serviceName)) {
                    try {
                        namingService.subscribe(serviceName, buildEventListener());
                        subscribedServices.add(serviceName);
                    } catch (NacosException e) {
                        log.error("订阅服务失败", e);
                    }
                }
            });
        } catch (NacosException e) {
            log.error("获取服务列表失败", e);
        }
    }

    /**
     * 获取所有服务实例
     * @return  服务实例列表
     */
    public List<ServiceInfo> getAllServiceInstances() {
        List<ServiceInfo> serviceInfos = new ArrayList<>();
        try {
            NamingService namingService = nacosServiceManager.getNamingService();
            List<String> services = namingService.getServicesOfServer(1, Integer.MAX_VALUE).getData();
            for (String serviceName : services) {
                List<Instance> onlineInstances = namingService.selectInstances(serviceName, true);
                // 下线服务暂时不用关注
                List<Instance> offlineInstances = namingService.selectInstances(serviceName, false);
                serviceInfos.add(new ServiceInfo(serviceName, String.valueOf(onlineInstances.size())));
            }
        } catch (NacosException e) {
            e.printStackTrace();
        }
        return serviceInfos;
    }



    public void sendMsg(String service) {
        SendMessage error = new SendMessage();
        error.setSecret(secret);
        error.setWebhook(webhook);
        error.setText(service);
        // 通过钉钉发送消息
        DingTalkUtil.sendMsg(error);
    }



}
